//From window listener, store key in array
function keyDown(e) {
		pressedKeys[e.keyCode || e.which] = true;
}

//From window listener, delete key in array
function keyUp(e) {
	pressedKeys[e.keycode || e.which] = false;
}

//Welcome state, also initializes the canvas
function welcome(canvas) {
	canvas.width = 800;
	canvas.height = 600;
	ctx = canvas.getContext("2d");
	
	ctx.fillRect(0,0,800,600);
	ctx.font="30px Arial";
	ctx.fillStyle="red";
	ctx.fillText("Press Enter to begin the war!", 220, canvas.height / 2);
	
	welcomeTime = setInterval(welcomeLoop, 30);
}

//Waits until user presses enter to start the main GameLoop
//Also assigns basic mechanic variables that don't change and leads into other variables
function welcomeLoop() {
	if (pressedKeys[13]) {
		clearInterval(welcomeTime);
		boundaries = {left: 0, right: 800, top: 0, bottom: 575};
		shipWidth = 45;
		shipHeight = 50;
        shipSpeed = 2;
		rocketWidth = 30;
		rocketHeight = 30;
		bombWidth = 30;
		bombHeight = 30;
		alienWidth = 55;
		alienHeight = 50;
		lastFire = null;
		ship = document.getElementById("ship");

		startTime = null;

		score = 0;
		level = 1;
        lives = 3;



		start();
	}
}

//Main game information, all variables are defined in this state
function start() {
	alienSpeed = 1.55 + 0.05 * level;
	alienDrop = 30.3 + 0.7 * level;
	alienPoints = 11 + 2 * level;
	bombRate = 0.0003 + 0.0002 * level;
	bombSpeed = 0.85 + 0.05 * level;
	rocketSpeed = 3 - 0.2 * level;
	rocketRate = 2 - 0.1 * level;
	
	alienRow = 1 + 1 * level;
	alienCol = 2 + 1 * level;
	aliens= [];
	rockets= [];
	bombs= [];
	
	x = canvas.width / 2;
	y = boundaries.bottom - 65 - shipHeight / 2;

	//Populates the aliens from the defined rows and columns and stores in array
	for (var i=0; i < alienRow; i++) {
		for (var j=0; j < alienCol; j++) {
			aliens.push(new makeAlien(0 + j * 55, 0 + i * 40, i, j));
		}
	}
	
	//Repeat the loop every 5 ms for smooth transitions and responces
    gameTime = setInterval(gameLoop, 20);
}

//Main game loop that runs the core of the game and calls all of the required functions
function gameLoop() {

	//Moves ship left with left-arrow key (37)
    if (pressedKeys[37]) {
    	if(x < boundaries.left + shipWidth / 2) {
    		x = x;
    	}
    	else {
    		x -= shipSpeed;
    	}
    }

    //Moves ship right with right-arrow key (39)
    if (pressedKeys[39]) {
    	if(x > boundaries.right - shipWidth / 2) {
    		x = x;
    	}
    	else {
    		x += shipSpeed;
    	}
    }

    //Creates an instance of a rocket and stores it in the rockets array when space is pressed (32)
    if (pressedKeys[32]) {
    	if (lastFire == null || ((new Date()).valueOf() - lastFire) > 1000 / rocketRate) {
    		rockets.push(new fireRocket(x,y));
    		lastFire = (new Date()).valueOf();
    	}

    }
    
    // clear canvas and draw ship to the canvas
    clearCanvas();
    ctx.drawImage(ship, x - shipWidth / 2, y - shipHeight / 2, shipWidth, shipHeight);

    //Create a HUD for the user to see their progress while ingame and update it as game progresses
	ctx.fillRect(0, 575, 800, 25);
    ctx.font="18px Arial";
    ctx.fillStyle="lime";
    ctx.fillText("Number of Lives: " + lives + 
    		"                          Current Score: " + score + 
    		"                          Current Level: " + level, 50,595);

    //Takes an instance of a rocket and checks whether it is out of the canvas, if so delete it
    //from the array. If not, keep moving it up the canvas
    for (var r = 0; r < rockets.length; r++) {
    	if (rockets[r].rockety < 0) {
    		rockets.splice(r, 1);
    	}
    	else {
    		rockets[r].rockety -= rocketSpeed;
    		ctx.drawImage(rockets[r].rocket, rockets[r].rocketx - rocketWidth / 2, 
    				rockets[r].rockety, rocketWidth, rocketHeight);
    	}
    }

    //Takes an instance of a bomb and checks whether it is out of the canvas, if so delete it
    //from the array. If not, keep moving it down the canvas
    for (var b = 0; b < bombs.length; b++) {
    	if (bombs[b].bomby > canvas.height) {
    		bombs.splice(b, 1);
    	}
    	else {
    		bombs[b].bomby += bombSpeed;
    		ctx.drawImage(bombs[b].bomb, bombs[b].bombx - bombWidth / 2,
    				bombs[b].bomby, bombWidth, bombHeight);
    	}
    }

    //Define variables for left, right and bottom alien representatives
    rightMost = -5;
    rightAlien = null;
    leftMost = 1000;
    leftAlien = null;
    bottomMost = -5;
    bottomAlien = null;

    //Stores the index of the left, right, and bottom most alien from the array
    for (var k = 0; k < aliens.length; k++) {
    	if (aliens[k].alienx > rightMost) {
    		rightMost = aliens[k].alienx;
    		rightAlien = k;
    	}
    	if (aliens[k].alienx < leftMost) {
    		leftMost = aliens[k].alienx;
    		leftAlien = k;
    	}
    	if (aliens[k].alieny > bottomMost) {
    		bottomMost = aliens[k].alieny;
    		bottomAlien = k;
    	}
    }

    //Store the resulting alien co-ordinates from the indexs gathered above
    leftAlien = aliens[leftAlien].alienx;
    rightAlien = aliens[rightAlien].alienx;
    bottomAlien = aliens[bottomAlien].alieny;

    //If the alien representative reaches one of the bounds, make the alien go
    //the other direction and also drop a level down the canvas
	if (rightAlien + alienSpeed + alienWidth >= boundaries.right || 
    					leftAlien + alienSpeed <= boundaries.left) {
    	alienSpeed = -alienSpeed;	    
	    for (var move = 0; move < aliens.length; move++) {
			aliens[move].alieny = aliens[move].alieny + alienDrop;
			ctx.drawImage(aliens[move].alienImg, aliens[move].alienx, aliens[move].alieny, alienWidth, alienHeight);
		}
	}

	//If the alien representative reaches the bottom of the canvas, gameOver! 
	//If not, then move the alien across the canvas
	if (bottomAlien + alienHeight > boundaries.bottom) {
		clearInterval(gameTime);
		gameOver();
	}
    else {
    	for (var move = 0; move < aliens.length; move++) {
    		aliens[move].alienx = aliens[move].alienx + alienSpeed;
    		ctx.drawImage(aliens[move].alienImg, aliens[move].alienx, aliens[move].alieny, alienWidth, alienHeight);
    	}
    }

    //Go through the aliens and current rockets and check whether any intersect eachother
    //If they do, remove the rocket, add to the score, and remove the killed alien
    for (var a = 0; a < aliens.length; a++) {
    	var alien = aliens[a];
    	var hit = false;

    	for (var r = 0; r < rockets.length; r++) {
    		var rocket = rockets[r];

    		if (rocket.rocketx >= alien.alienx && rocket.rocketx <= alien.alienx + alienWidth && rocket.rockety >= alien.alieny && rocket.rockety <= alien.alieny + alienHeight) {
				 rockets.splice(r--, 1);
				 hit = true;
				score += alienPoints;
				break;
			}
    	}

    	if(hit) {
    		aliens.splice(a--, 1);
    	}
    }

    //Store the front row of aliens in each column so that they may later fire 
    frontAliens = {};
    for (var f = 0; f < aliens.length; f++) {
    	var alien = aliens[f];

    	if (!frontAliens[alien.alienCol] || frontAliens[alien.alienCol].alienRow < alien.alienRow) {
    		frontAliens[alien.alienCol] = alien;
    	}
    }

    //Using randomization, if bombRate is higher then the generated number it will fire a bomb randomly
    for (var f = 0; f < alienCol; f++) {
    	var alien = frontAliens[f];
    	var fire = bombRate;
    	if (fire > Math.random()) {
    		bombs.push(new bombDrop(alien.alienx + alienWidth / 2, alien.alieny));
    	}
    }

    //If any of the bombs intersect with the ship co-ordinates, it takes away a life from the user
    for (var v = 0; v < bombs.length; v++) {
    	var bomb = bombs[v];
    	if (bomb.bombx >= (x - shipWidth / 2) && bomb.bombx <= (x + shipWidth / 2) && bomb.bomby >= (y - shipHeight / 2) && bomb.bomby <= (y + shipHeight / 2)) {
    		bombs.splice(v--, 1);
    		if (lives > 0) {
    			lives -= 1;
    		}
    	}
    }

    //If any of the aliens intersect with the ship co-ordinates, it is gameOver!
    for (var a = 0; a < aliens.length; a++) {
    	var alien = aliens[a];
    	if ((alien.alienx + alienWidth / 2) > (x - shipWidth / 2) &&
    		(alien.alienx - alienWidth / 2) < (x + shipWidth / 2) &&
    		(alien.alieny + alienHeight / 2) > (y - shipHeight / 2) &&
    		(alien.alieny - alienHeight / 2) < (y + shipHeight / 2)) {
    		lives = 0;
    	}
    }

    //If player removes all aliens from the array, the user increases level, wins current
    //level and adds score for completion
    if (aliens.length == 0) {
    	score += level * 100;
    	level += 1;
    	clearInterval(gameTime);
    	levelIntro();
    }

    //Call the pause function to pause the game using p
    if (pressedKeys[80]) {
        clearInterval(gameTime);
        pauseTime = setInterval("pause()", 20);
    }

    //If player reaches 0 lives, stop the loop and display gameOver state
    if (lives == 0) {
    	bombs = [];
    	clearInterval(gameTime);
    	gameOver();
    }
}

//Clear the canvas so that it can be upgraded
function clearCanvas() {
    canvas.width = canvas.width;
}

//Creates a rocket object with co-ordinates
function fireRocket(x, y) {
	this.rocket = document.getElementById("rocket");
	this.rocketx = x;
	this.rockety = y;
}

//Creates a bomb object with co-ordinates
function bombDrop(x, y) {
	this.bomb = document.getElementById("bomb");
	this.bombx = x;
	this.bomby = y;
}

//Creates an alien object with co-ordinates and it's row and column
function makeAlien(posx, posy, row, col) {
	this.alienImg = document.getElementById("alien");
	this.alienx = posx;
	this.alieny = posy;
	this.alienRow = row;
	this.alienCol = col;
}

//The levelIntro state, once the user wins a level, this sets up the next level
//and allows for a break before the aliens start again
function levelIntro() {
	ctx.fillStyle="black";
	ctx.fillRect(0,0,800,600);
	ctx.font="90px Arial";
	ctx.fillStyle="red";
	ctx.fillText("Level: " + level, 150, canvas.height / 2);
 	setTimeout(start, 3000);
}

//The pause state, that displays a paused indication and freezes gameLoop
function pause() {
    ctx.fillStyle="black";
    ctx.fillRect(0,0,800,600);
    ctx.font="90px Arial";
    ctx.fillStyle="red";
    ctx.fillText("GAME PAUSED", 90, canvas.height / 2);
    ctx.font="40px Arial";
    ctx.fillStyle="lime";
    ctx.fillText("Press u to unpause the game!", 160, 400);
    //Press u to unpause the game
    if (pressedKeys[85]) {
        clearCanvas();
        clearInterval(pauseTime);
        gameTime = setInterval("gameLoop()", 10);
    }
}

//The gameOver state, that displays the user's score and level that was reached
function gameOver() {
	ctx.fillStyle="black";
	ctx.fillRect(0,0,800,600);
	ctx.font="90px Arial";
	ctx.fillStyle="red";
	ctx.fillText("GAMEOVER", 150, canvas.height / 4);
	ctx.font="40px Arial";
	ctx.fillStyle="lime";
	ctx.fillText("Final Score:  " + score, 280, 330);
	ctx.fillText("Final Level:  " + level, 285, 270);
	ctx.font="30px Arial";
	ctx.fillStyle="red";
	ctx.fillText("Press Enter to try again and beat the Alien Invasion!", 58, 455);
	welcomeTime = setInterval(welcomeLoop, 10);
}